;;======================================================================================================================
;;///// test_malloc.asm //////////////////////////////////////////////////////////////////////////////////// GPLv2 /////
;;======================================================================================================================
;; (c) 2009 KolibriOS team <http://kolibrios.org/>
;;======================================================================================================================
;; This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
;; License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later
;; version.
;;
;; This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License along with this program. If not, see
;; <http://www.gnu.org/licenses/>.
;;======================================================================================================================
;? Tests of malloc()/free() from the kernel heap.
;? This file is not included in the kernel, it is just test application.
;;======================================================================================================================

use32
db 'MENUET01'
dd 1, start, i_end, mem, mem, 0, 0

;-----------------------------------------------------------------------------------------------------------------------
start: ;////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
        ; Zero-initialize uglobals (as in kernel at boot)
        mov     ecx, (zeroend - zerostart + 3) / 4
        xor     eax, eax
        mov     edi, zerostart
        rep
        stosd
        ; Initialize small heap (as in kernel at boot)
        call    init_malloc
        ; Run tests
        call    run_test1
        call    run_test2
        call    run_test3
        ; All is OK, return
        or      eax, -1
        int     0x40

;-----------------------------------------------------------------------------------------------------------------------
run_test1: ;////////////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
;? basic test
;-----------------------------------------------------------------------------------------------------------------------
        mov     eax, 1
        call    malloc_with_test
        mov     byte[eax], 0xdd
        mov     esi, eax
        mov     eax, 1
        call    malloc_with_test
        cmp     byte[esi], 0xdd
        jnz     memory_destroyed
        mov     byte[eax], 0xee
        xchg    eax, esi
        call    free
        cmp     byte[esi], 0xee
        jnz     memory_destroyed
        xchg    eax, esi
        call    free
        ret

;-----------------------------------------------------------------------------------------------------------------------
run_test2: ;////////////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
        ret

;-----------------------------------------------------------------------------------------------------------------------
run_test3: ;////////////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
;? 1024000 times run random operation.
;? Randomly select malloc(random size from 1 to 1023) or free(random of previously allocated areas)
;-----------------------------------------------------------------------------------------------------------------------
        mov     edi, 0x12345678
        xor     esi, esi ; 0 areas allocated
        mov     ebx, 1024000

  .loop:
        imul    edi, 1103515245
        add     edi, 12345
        mov     eax, edi
        shr     eax, 16
        test    ebx, 64
        jz      .prefer_free

  .prefer_malloc:
        test    eax, 3
        jz      .free
        jmp     @f

  .prefer_free:
        test    eax, 3
        jnz     .free

    @@: shr     eax, 2
        and     eax, 1023
        jz      .loop
        push    ebx
        push    eax
;       mov     ecx, [saved_state_num]
;       mov     [saved_state + ecx * 8], eax
        call    malloc_with_test
;       mov     ecx, [saved_state_num]
;       mov     [saved_state + ecx * 8 + 4], eax
;       inc     [saved_state_num]
        pop     ecx
        pop     ebx
        inc     esi
        push    ecx eax
        push    edi
        mov     edi, eax
        mov     eax, esi
        rep
        stosb
        pop     edi
        jmp     .common
.free:
        test    esi, esi
        jz      .loop
        xor     edx, edx
        div     esi
        sub     edx, esi
        neg     edx
        dec     edx
        mov     eax, [esp + edx * 8]
;       mov     ecx, [saved_state_num]
;       mov     [saved_state + ecx * 8], -1
;       mov     [saved_state + ecx * 8 + 4], eax
;       inc     [saved_state_num]
        mov     ecx, [esp + edx * 8 + 4]
        push    edi eax
        mov     edi, eax
        mov     al, [edi]
        repz
        scasb
        jnz     memory_destroyed
        pop     eax edi
        push    ebx edx
        call    free
        pop     edx ebx
        dec     esi
        pop     eax ecx
        push    edi
        lea     edi, [esp + 4]

    @@: dec     edx
        js      @f
        xchg    eax, [edi]
        xchg    ecx, [edi + 4]
        add     edi, 8
        jmp     @b

   @@:  pop     edi

  .common:
        dec     ebx
        jnz     .loop

    @@: dec     esi
        js      @f
        pop     eax ecx
        call    free
        jmp     @b

    @@: ret

;-----------------------------------------------------------------------------------------------------------------------
malloc_with_test: ;/////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
;? calls malloc() and checks returned value
;-----------------------------------------------------------------------------------------------------------------------
        call    malloc
        test    eax, eax
        jz      generic_malloc_fail
        call    check_mutex
        call    check_range
        ret

; Stubs for kernel procedures used by heap code

;-----------------------------------------------------------------------------------------------------------------------
wait_mutex: ;///////////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
        inc     dword[ebx]
        ret

;-----------------------------------------------------------------------------------------------------------------------
kernel_alloc: ;/////////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
        cmp     dword[esp + 4], bufsize
        jnz     error1
        mov     eax, buffer
        ret     4

; Error handlers

;-----------------------------------------------------------------------------------------------------------------------
error1: ;///////////////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
        mov     eax, 1
        jmp     error_with_code

;-----------------------------------------------------------------------------------------------------------------------
generic_malloc_fail: ;//////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
        mov     eax, 2
        jmp     error_with_code

;-----------------------------------------------------------------------------------------------------------------------
check_mutex: ;//////////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
        cmp     [mst.mutex], 0
        jnz     @f
        ret

    @@: mov     eax, 3
        jmp     error_with_code

;-----------------------------------------------------------------------------------------------------------------------
check_range: ;//////////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
        cmp     eax, buffer
        jb      @f
        cmp     eax, buffer+bufsize
        jae     @f
        ret

    @@: mov     eax, 4
        jmp     error_with_code

;-----------------------------------------------------------------------------------------------------------------------
memory_destroyed: ;/////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
        mov     eax, 5
        jmp     error_with_code

;-----------------------------------------------------------------------------------------------------------------------
error_with_code: ;//////////////////////////////////////////////////////////////////////////////////////////////////////
;-----------------------------------------------------------------------------------------------------------------------
;? eax = error code
;-----------------------------------------------------------------------------------------------------------------------
;# Error codes:
;#   1 - error in testing code (wrong bufsize)
;#   2 - malloc() returned NULL
;#   3 - mutex not released
;#   4 - weird returned value from malloc()
;#   5 - memory destroyed by malloc() or free()
;-----------------------------------------------------------------------------------------------------------------------
        mov     edx, saved_state_num
        int3    ; simplest way to report error
        jmp     $ - 1 ; just in case

; Include main heap code
include "include/proc32.inc"
include "include/struct.inc"
include "include/kernel.inc"
include "include/const.inc"
include "core/malloc.asm"

i_end:

align 4
zerostart:
mst memory_state_t

align 16
bufsize = 0x40000 ; change if malloc.inc changes
buffer rb bufsize

zeroend:

saved_state_num dd ?
saved_state     rd 0x10000

align 4
rb 0x10000 ; for stack

mem:
